package name.cyrax.red.jpv.impl.adapters;

import name.cyrax.red.jpv.api.adapters.CalculationContext;
import name.cyrax.red.jpv.api.adapters.DoubleAdapter;
import name.cyrax.red.jpv.api.adapters.IntegerAdapter;
import name.cyrax.red.jpv.api.adapters.LongAdapter;
import name.cyrax.red.jpv.api.adapters.NumberAdapter;
import name.cyrax.red.jpv.api.adapters.NumericAdapter;
import name.cyrax.red.jpv.api.adapters.OmniNumberAdapter;
import name.cyrax.red.jpv.api.adapters.PrepareAdapter;
import name.cyrax.red.jpv.api.funcs.FunctorInstance;
import name.cyrax.red.jpv.api.funcs.info.ValueType;
import name.cyrax.red.jpv.impl.funcs.DefaultFunctorInstances;

public abstract class NumericImpl<N extends Number> implements 
	OmniNumberAdapter<N>, NumericAdapter, DoubleAdapter, IntegerAdapter, LongAdapter, NumberAdapter<N>, 
	PrepareAdapter
{

	protected final boolean isValidIndex(int index)
	{
		return index >= 0 && index < size(); 
	}
	
	protected final void checkIndex(int index)
	{
		if (!isValidIndex(index))
			throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
	}

	private String outOfBoundsMsg(int index)
	{
        return "Index: " + index + ", Size: " + size();
	}

	@Override public IntegerAdapter asInteger()
	{
		return this;
	}

	@Override public LongAdapter asLong()
	{
		return this;
	}

	@Override public DoubleAdapter asDouble()
	{
		return this;
	}

	@Override public NumberAdapter<N> asNumber()
	{
		return this;
	}
	
	@Override public String toString()
	{
		return NumericAdapters.toString(this).toString();
	}
	
	@Override public PrepareAdapter prepare()
	{
		return null; //we dont need prepare by default
	}
	
	@Override public void beginCalc(CalculationContext context)
	{
	}
	
	@Override public void endCalc()
	{
	}
	
	public abstract ValueType getUnderlyingType();
	
	protected final FunctorInstance getDefaultFunctorInstance()
	{
		return DefaultFunctorInstances.ADAPTER.getFunctorInstance(
			DefaultFunctorInstances.FUNCTOR_STUB_ADAPTER, this, getUnderlyingType());
	}
	
	public static <T> T switchOf(int index, T p0) {
		return index == 0 ? p0 : null;
	}

	public static <T> T switchOf(int index, T p0, T p1) {
		if (index == 0) return p0;
		if (index == 1) return p1;
		return null;
	}

	@SafeVarargs
	public static <T> T switchOf(int index, T p0, T... pN) {
		if (index == 0) return p0;
		if (index > 0 && index <= pN.length) return pN[index - 1];
		return null;
	}
}
